/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package x10.interfaces.cm15a.receive;

import java.util.BitSet;
import x10.enums.Device;
import x10.enums.Function;
import x10.enums.House;
import x10.enums.Interfaces;
import x10.enums.Media;
import x10.event.X10Command;
import x10.utilities.BitSetUtils;
import x10.utilities.ChunkOfBytes;

/**
 *
 * @author Denny
 */
public class RFPowerLineMessage {
    LastAddressedTracker lat;
    private byte[] message;
    private BitSet codesBitSet;

    private int codesHighByte = 2;
    private int codesLowByte = 4;

    private int houseCodeBit1 = 0;
    private int houseCodeBit2 = 1;
    private int houseCodeBit4 = 2;
    private int houseCodeBit8 = 3;

    private int functionOrAddressBit = 8;

    private int unitCodeBit1 = 11;
    private int unitCodeBit2 = 12;
    private int unitCodeBit4 = 9;
    private int unitCodeBit8 = 5;

    private int functionOnOffBit = 10;
    private int functionDimBrightBit = 11;
    private int functionAllUnitsOnOffBit = 11;
    private int functionDimBrightCommandBit = 12;

    public RFPowerLineMessage(ChunkOfBytes bytes, LastAddressedTracker lat){
        this.message = bytes.getBytes();
        this.lat = lat;
        codesBitSet = getCodesBitSet();
    }

    public X10Command parse(){
        int dimBrightLevel = 0; //RF version of the PL message doesn't transmit dim or bright level
        X10Command x10Command = new X10Command(getHouseCode(), getDeviceCode(), getFunctionCode(), dimBrightLevel, Media.RADIO_FREQUENCY, Interfaces.CM15A.toString());
        return x10Command;
    }

    private House getHouseCode(){
        int houseCode = (int)getCodeNibble(houseCodeBit8, houseCodeBit4, houseCodeBit2, houseCodeBit1);
        return House.getHouseFromPowerline(houseCode);
    }

    private Device getDeviceCode(){
        //RF Unit Code is not encoded like it is for powerline ... just add 1 to the bit value and use the actual value
        Device retDevice = null;

        if(isAddress()){
            int unitCode = (1 + (int)getCodeNibble(unitCodeBit8, unitCodeBit4, unitCodeBit2, unitCodeBit1));
            retDevice = Device.getDeviceFromOrdinal(unitCode);
        } else {
            retDevice = lat.getLastAddressed(getHouseCode());
        }
        return retDevice;
    }

    private Function getFunctionCode(){
        //Code set not complete ... haven't completely mapped the function address space
        Function retFunction = null;
        if(isAddress()){
            if(codesBitSet.get(functionOnOffBit)){
                retFunction = Function.OFF;
            } else {
                retFunction = Function.ON;
            }
        } else if (isFunction()){
            if(codesBitSet.get(functionDimBrightCommandBit)){
                if(codesBitSet.get(functionDimBrightBit)){
                    retFunction = Function.DIM;
                } else {
                    retFunction = Function.BRIGHT;
                }
            } else {
                if(codesBitSet.get(functionAllUnitsOnOffBit)){
                    retFunction = Function.ALL_LIGHTS_ON;
                } else {
                    retFunction = Function.ALL_UNITS_OFF;
                }
            }
        } else {
            return Function.UNKNOWN;
        }
        return retFunction;
    }

    private boolean isFunction(){
        return codesBitSet.get(functionOrAddressBit);
    }

    //Redundant, but complementary to isFunction() method
    private boolean isAddress(){
        if(codesBitSet.get(functionOrAddressBit)){
            return false;
        } else {
            return true;
        }
    }

    private byte getCodeNibble(int bit8, int bit4, int bit2, int bit1){
        //returns nibble assembled from bit offsets into codeBitSet
        //returns the nibble in the lower half of the byte
        BitSet houseCodeBitSet = new BitSet(8);  //system dependent on what size it creates, but minimum of 8 bits

        if(codesBitSet.get(bit1)){
            houseCodeBitSet.set(7);
        }
        if(codesBitSet.get(bit2)){
            houseCodeBitSet.set(6);
        }
        if(codesBitSet.get(bit4)){
            houseCodeBitSet.set(5);
        }
        if(codesBitSet.get(bit8)){
            houseCodeBitSet.set(4);
        }

        return BitSetUtils.getByte(houseCodeBitSet, 0);
        
    }

    private BitSet getCodesBitSet(){
        byte[] bytesToProcess = new byte[2];

        bytesToProcess[0] = message[codesHighByte];
        bytesToProcess[1] = message[codesLowByte];

        return BitSetUtils.bytesToBitSet(bytesToProcess);

    }
}
